home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / inventor / noodle / GeneralizedCylinder.c++ < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  56.5 KB  |  1,866 lines

  1. /*
  2.  * Copyright (C) 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.  |   Classes:
  19.  |      GeneralizedCylinder
  20.  |
  21.  |   Author(s)          : Paul Isaacs
  22.  |
  23.  */
  24.  
  25. #include <stdio.h>
  26. #include <Inventor/SbLinear.h>
  27. #include <Inventor/SoDB.h>
  28. #include <Inventor/nodekits/SoAppearanceKit.h>
  29. #include <Inventor/nodes/SoGroup.h>
  30. #include <Inventor/nodes/SoSeparator.h>
  31. #include <Inventor/nodes/SoShapeHints.h>
  32. #include <Inventor/nodes/SoCoordinate3.h>
  33. #include <Inventor/nodes/SoFaceSet.h>
  34. #include <Inventor/nodes/SoIndexedFaceSet.h>
  35. #include <Inventor/nodes/SoMaterial.h>
  36. #include <Inventor/nodes/SoQuadMesh.h>
  37. #include <Inventor/nodes/SoSwitch.h>
  38. #include <Inventor/nodes/SoTextureCoordinateBinding.h>
  39. #include <Inventor/nodes/SoTextureCoordinate2.h>
  40. #include <Inventor/nodes/SoTexture2Transform.h>
  41. #include <Inventor/nodes/SoTexture2.h>
  42. #include <Inventor/nodes/SoTransform.h>
  43. #include <Inventor/nodes/SoTriangleStripSet.h>
  44.  
  45. #include <Inventor/manips/SoTransformManip.h>
  46. #include <Inventor/draggers/SoDragger.h>
  47. #include <Inventor/nodes/SoSurroundScale.h>
  48.  
  49. #include <Inventor/actions/SoWriteAction.h>
  50.  
  51. #include <Inventor/sensors/SoNodeSensor.h>
  52.  
  53. #include "GeneralizedCylinder.h"
  54. #include "Triangulator.h"
  55. #include "NurbMaker.h"
  56. #include <Inventor/nodes/SoVertexShape.h>
  57.  
  58.  
  59. SO_KIT_SOURCE(GeneralizedCylinder);
  60.  
  61.  
  62. ////////////////////////////////////////////////////////////////////////
  63. //
  64. // Description:
  65. //    This initializes the GeneralizedCylinder class.
  66. //
  67. // Use: internal
  68.  
  69. void
  70. GeneralizedCylinder::initClass()
  71. //
  72. ////////////////////////////////////////////////////////////////////////
  73. {
  74.     SO__KIT_INIT_CLASS(GeneralizedCylinder, "GeneralizedCylinder",
  75.             SoSeparatorKit);
  76. }
  77.  
  78. ////////////////////////////////////////////////////////////////////////
  79. //
  80. // Description:
  81. //    Constructor
  82. //
  83. // Use: public
  84.  
  85. GeneralizedCylinder::GeneralizedCylinder()
  86. //
  87. ////////////////////////////////////////////////////////////////////////
  88. {
  89.     SO_KIT_CONSTRUCTOR(GeneralizedCylinder);
  90.  
  91.     isBuiltIn = TRUE;
  92.  
  93.     SO_KIT_ADD_FIELD(renderShapeType,    (FACE_SET));
  94.     SO_KIT_ADD_FIELD(normsFlipped,       (FALSE));
  95.     SO_KIT_ADD_FIELD(profileClosed,      (FALSE));
  96.     SO_KIT_ADD_FIELD(crossSectionClosed, (FALSE));
  97.     SO_KIT_ADD_FIELD(spineClosed,        (FALSE));
  98.     SO_KIT_ADD_FIELD(twistClosed,        (FALSE));
  99.     SO_KIT_ADD_FIELD(minNumRows,         (1));
  100.     SO_KIT_ADD_FIELD(minNumCols,         (1));
  101.     SO_KIT_ADD_FIELD(withSides,          (TRUE));
  102.     SO_KIT_ADD_FIELD(withTopCap,         (TRUE));
  103.     SO_KIT_ADD_FIELD(withBottomCap,      (TRUE));
  104.     SO_KIT_ADD_FIELD(withTextureCoords,  (FALSE));
  105.  
  106.     SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType,    FACE_SET);
  107.     SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType,    TRIANGLE_STRIP_SET);
  108.     SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType,    QUAD_MESH);
  109.     SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType,    BEZIER_SURFACE);
  110.     SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType,    CUBIC_SPLINE_SURFACE);
  111.     SO_KIT_DEFINE_ENUM_VALUE(RenderShapeType,    CUBIC_TO_EDGE_SURFACE);
  112.     SO_KIT_SET_SF_ENUM_TYPE(renderShapeType,    RenderShapeType);
  113.  
  114.     // The input half of the scene graph
  115.     SO_KIT_ADD_CATALOG_ENTRY(inputSwitch,SoSwitch,TRUE,topSeparator,childList,FALSE );
  116.     SO_KIT_ADD_CATALOG_ENTRY(profileCoords,SoCoordinate3,TRUE,inputSwitch,,TRUE );
  117.     SO_KIT_ADD_CATALOG_ENTRY(crossSectionCoords,SoCoordinate3,TRUE,inputSwitch,,TRUE );
  118.     SO_KIT_ADD_CATALOG_ENTRY(spineCoords,SoCoordinate3,TRUE,inputSwitch,,TRUE );
  119.     SO_KIT_ADD_CATALOG_ENTRY(twistCoords,SoCoordinate3,TRUE,inputSwitch,,TRUE );
  120.  
  121.     // The output half of the scene graph
  122.     // ( the shape produced during 'updateSurface' based on the contents
  123.     // of the input half of the scene graph
  124.     SO_KIT_ADD_CATALOG_ENTRY(outputGrp,SoGroup,TRUE,topSeparator,childList,FALSE );
  125.     SO_KIT_ADD_CATALOG_ENTRY(shapeHints,SoShapeHints,TRUE,outputGrp,,TRUE );
  126.     SO_KIT_ADD_CATALOG_ENTRY(textureBinding,SoTextureCoordinateBinding,TRUE,outputGrp,,TRUE );
  127.     SO_KIT_ADD_CATALOG_ENTRY(textureCoords,SoTextureCoordinate2,TRUE,outputGrp,,TRUE );
  128.     SO_KIT_ADD_CATALOG_ENTRY(texture2,SoTexture2,TRUE,outputGrp,,TRUE );
  129.     SO_KIT_ADD_CATALOG_ENTRY(coords,SoCoordinate3,TRUE,outputGrp,,TRUE);
  130.     SO_KIT_ADD_CATALOG_ENTRY(faceSet,SoFaceSet,TRUE,outputGrp,,TRUE);
  131.     SO_KIT_ADD_CATALOG_ENTRY(quadMesh,SoQuadMesh,TRUE,outputGrp,,TRUE);
  132.     SO_KIT_ADD_CATALOG_ENTRY(nurbsSurfaceGroup,SoGroup,TRUE,outputGrp,,TRUE);
  133.     SO_KIT_ADD_CATALOG_ENTRY(triangleStripSet,SoTriangleStripSet,TRUE,outputGrp,,TRUE);
  134.     SO_KIT_ADD_CATALOG_ENTRY(capTextureBinding,SoTextureCoordinateBinding,TRUE,outputGrp,,TRUE );
  135.     SO_KIT_ADD_CATALOG_ENTRY(capTextureCoords,SoTextureCoordinate2,TRUE,outputGrp,,TRUE );
  136.     SO_KIT_ADD_CATALOG_ENTRY(topCapCoords,SoCoordinate3,TRUE,outputGrp,,TRUE );
  137.     SO_KIT_ADD_CATALOG_ENTRY(topCapFaces,SoIndexedFaceSet,TRUE,outputGrp,,TRUE );
  138.     SO_KIT_ADD_CATALOG_ENTRY(bottomCapCoords,SoCoordinate3,TRUE,outputGrp,,TRUE );
  139.     SO_KIT_ADD_CATALOG_ENTRY(bottomCapFaces,SoIndexedFaceSet,TRUE,outputGrp,,TRUE );
  140.  
  141.     SO_KIT_INIT_INSTANCE();
  142.  
  143.  
  144.     //////////////////////////////////////////
  145.     // Set up the input half of the subgraph
  146.     //////////////////////////////////////////
  147.     SoSwitch *crvSwitch = new SoSwitch;
  148.     crvSwitch->whichChild = SO_SWITCH_NONE;
  149.     setAnyPart( "inputSwitch",crvSwitch,TRUE );
  150.  
  151.     setPart( "profileCoords",new SoCoordinate3 );
  152.     setPart( "crossSectionCoords",new SoCoordinate3 );
  153.     setPart( "spineCoords", new SoCoordinate3 );
  154.     setPart( "twistCoords", new SoCoordinate3 );
  155.  
  156.     // Set the default values for these curves...
  157.     set("profileCoords      { point [ 1 -1 0, 1 1  0 ] }" );
  158.     set("crossSectionCoords { point [ 1  0 1, 1 0 -1, -1 0 -1, -1 0 1 ] }" );
  159.     set("spineCoords        { point [ 0 -1 0, 0 1  0 ] }" );
  160.     set("twistCoords        { point [ 0 -1 0, 0 1  0 ] }" );
  161.  
  162.  
  163.  
  164.     //////////////////////////////////////////
  165.     // Set up the flags and parameters
  166.     //////////////////////////////////////////
  167.  
  168.     fullProfile = new SoCoordinate3;
  169.     fullCrossSection = new SoCoordinate3;
  170.     fullProfile->ref();
  171.     fullCrossSection->ref();
  172.  
  173.     profileAlreadyClosed = FALSE;
  174.     crossSectionAlreadyClosed = FALSE;
  175.     spineAlreadyClosed = FALSE;
  176.     twistAlreadyClosed = FALSE;
  177.     alreadyWithTextureCoords = FALSE;
  178.  
  179.     //////////////////////////////////////////
  180.     // Set up the output half of the subgraph
  181.     // that needs to be ready ahead of time.
  182.     //////////////////////////////////////////
  183.  
  184.     //
  185.     // This combination of shape hints will turn on two-sided lighting
  186.     // for us (if it is available)
  187.     //
  188.     SoShapeHints *hints = new SoShapeHints;
  189.     hints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE;
  190.     hints->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE;
  191.     hints->faceType = SoShapeHints::CONVEX; 
  192.  
  193.     hints->creaseAngle = M_PI/6.0;
  194.     setPart( "shapeHints", hints );
  195.  
  196.     topCapScratchFace = new SoIndexedFaceSet;
  197.     topCapScratchFace->ref();
  198.  
  199.     bottomCapScratchFace = new SoIndexedFaceSet;
  200.     bottomCapScratchFace->ref();
  201.  
  202.     numAllocedSpineKeyRots = 0;
  203.     spineKeyPointRotations = NULL;
  204.     spineParamDistances = NULL;
  205.  
  206.     // Initialize our nurbMaker
  207.     myNurbMaker = NULL;
  208.  
  209.     selfSensor = new SoNodeSensor( GeneralizedCylinder::fieldsChangedCB, this);
  210.     selfSensor->setPriority(0);
  211.  
  212.     inputSensor = new SoNodeSensor( GeneralizedCylinder::inputChangedCB, this);
  213.     inputSensor->setPriority(0);
  214.  
  215.     setUpConnections(TRUE,TRUE);
  216. }
  217.  
  218. //    detach/attach any sensors, callbacks, and/or field connections.
  219. //    Called by:            start/end of SoBaseKit::readInstance
  220. //    and on new copy by:   start/end of SoBaseKit::copy.
  221. //    Classes that redefine must call setUpConnections(TRUE,TRUE) 
  222. //    at end of constructor.
  223. //    Returns the state of the node when this was called.
  224. SbBool
  225. GeneralizedCylinder::setUpConnections( SbBool onOff, SbBool doItAlways )
  226. {
  227.     if ( !doItAlways && connectionsSetUp == onOff)
  228.     return onOff;
  229.  
  230.     if ( onOff ) {
  231.     // We connect AFTER base class.
  232.     SoSeparatorKit::setUpConnections( onOff, doItAlways );
  233.  
  234.     // Turn sensors on...
  235.     SoNode *iSw = inputSwitch.getValue();
  236.     if (iSw && inputSensor->getAttachedNode() != iSw)
  237.         inputSensor->attach(iSw);
  238.     if (selfSensor->getAttachedNode() != this)
  239.         selfSensor->attach(this);
  240.     updateSurface();
  241.     }
  242.     else {
  243.  
  244.     // We disconnect BEFORE base class.
  245.     if (selfSensor->getAttachedNode())
  246.         selfSensor->detach();
  247.     if (inputSensor->getAttachedNode())
  248.         inputSensor->detach();
  249.  
  250.     SoSeparatorKit::setUpConnections( onOff, doItAlways );
  251.     }
  252.     return !(connectionsSetUp = onOff);
  253. }
  254.  
  255. void    
  256. GeneralizedCylinder::setDefaultOnNonWritingFields()
  257. {
  258.     // Before writing, call setDefault(TRUE) on all parts 
  259.     // under the output group.
  260.     outputGrp.setDefault(TRUE);
  261.     shapeHints.setDefault(TRUE);
  262.     textureBinding.setDefault(TRUE);
  263.     textureCoords.setDefault(TRUE);
  264.     texture2.setDefault(TRUE);
  265.     coords.setDefault(TRUE);
  266.     faceSet.setDefault(TRUE);
  267.     quadMesh.setDefault(TRUE);
  268.     nurbsSurfaceGroup.setDefault(TRUE);
  269.     triangleStripSet.setDefault(TRUE);
  270.     capTextureBinding.setDefault(TRUE);
  271.     capTextureCoords.setDefault(TRUE);
  272.     topCapCoords.setDefault(TRUE);
  273.     topCapFaces.setDefault(TRUE);
  274.     bottomCapCoords.setDefault(TRUE);
  275.     bottomCapFaces.setDefault(TRUE);
  276.  
  277.     // Call base class...
  278.     SoSeparatorKit::setDefaultOnNonWritingFields();
  279. }
  280.  
  281. //
  282. // Destructor.  Must unref everything reffed in the constructor.
  283. //
  284. GeneralizedCylinder::~GeneralizedCylinder()
  285. {
  286.     if ( topCapScratchFace != NULL) {
  287.     topCapScratchFace->unref();
  288.     topCapScratchFace = NULL;
  289.     }
  290.     if ( bottomCapScratchFace != NULL) {
  291.     bottomCapScratchFace->unref();
  292.     bottomCapScratchFace = NULL;
  293.     }
  294.  
  295.     if ( fullProfile != NULL ) {
  296.     fullProfile->unref();
  297.     fullProfile = NULL;
  298.     }
  299.     if ( fullCrossSection != NULL ) {
  300.     fullCrossSection->unref();
  301.     fullProfile = NULL;
  302.     }
  303.  
  304.      if ( selfSensor != NULL ) {
  305.     delete selfSensor;
  306.     selfSensor = NULL;
  307.      }
  308.  
  309.      if ( inputSensor != NULL ) {
  310.     delete inputSensor;
  311.     inputSensor = NULL;
  312.      }
  313.      if (myNurbMaker != NULL)
  314.     delete myNurbMaker;
  315. }
  316.  
  317.  
  318. void 
  319. GeneralizedCylinder::loadRow( int rowNum, SbVec3f *newCoords )
  320. {
  321.     float   scale, paramDist;
  322.     SbVec3f center;
  323.     SbRotation rot;
  324.     SbRotation twistRot;
  325.  
  326.     scale = fullProfile->point[rowNum][0];
  327.     paramDist = (fullProfile->point[rowNum][1] - profileMinY) 
  328.         * profileOverHeight;
  329.  
  330.     getSpineInfo( paramDist, center, rot );
  331.  
  332.     getTwistInfo( paramDist, twistRot );
  333.  
  334.     SbMatrix changer, temp;
  335.     changer.setRotate( twistRot );
  336.     temp.setScale( scale );
  337.     changer.multRight( temp );
  338.  
  339.     temp.setRotate( rot );
  340.  
  341.     changer.multRight( temp );
  342.     temp.setTranslate( center );
  343.     changer.multRight( temp );
  344.  
  345.     int numCrossPts = fullCrossSection->point.getNum();
  346.     for ( int i = 0; i < numCrossPts; i++ ) 
  347.     changer.multVecMatrix( fullCrossSection->point[i], newCoords[i] );
  348. }
  349.  
  350. void 
  351. GeneralizedCylinder::loadTextureRow( int rowNum, SbVec2f *newCoords )
  352. {
  353.     float     uVal, vVal;
  354.     SoMFVec3f *cSC = &fullCrossSection->point;
  355.  
  356.     if ( rowNum == -1 ) {
  357.     // special case.  This is for the caps of the object.
  358.     for ( int i = 0; i < cSC->getNum(); i++ )  {
  359.         uVal = ( (*cSC)[i][0] - crossSectionMinX ) / crossSectionWidth;
  360.         vVal = 1 - (( (*cSC)[i][2] - crossSectionMinZ) / crossSectionDepth);
  361.         newCoords[i].setValue( uVal, vVal );
  362.     }
  363.     return;
  364.     }
  365.  
  366.     // vVal is distance parametrically along the spine...
  367.     vVal = (fullProfile->point[rowNum][1] - profileMinY) 
  368.         * profileOverHeight;
  369.  
  370.     SbVec3f   diff;
  371.     int       numCrossPts = cSC->getNum();
  372.     float     thisLength = 0.0;
  373.     for ( int i = 0; i < numCrossPts; i++ )  {
  374.     if ( i > 0 ) {
  375.         diff = (*cSC)[i] - (*cSC)[i-1];
  376.         thisLength += diff.length();
  377.     }
  378.  
  379.     uVal = thisLength / crossSectionLength;
  380.  
  381.     newCoords[i].setValue( uVal, vVal );
  382.     }
  383. }
  384.  
  385. void 
  386. GeneralizedCylinder::getSpineInfo( float paramDist, SbVec3f ¢er, 
  387.                    SbRotation &rot )
  388. {
  389.     if ( spineLength == 0.0 ) {
  390.     center = SbVec3f( 0,paramDist * profileHeight,0);
  391.     rot = SbRotation::identity();
  392.     return;
  393.     }
  394.     
  395.     float desiredDist = paramDist * spineLength;
  396.  
  397.     float lengthSoFar = 0.0;
  398.     float nextLength = 0.0;
  399.     SbVec3f p1, p2, diff, norm1, norm2, norm;
  400.     SbVec3f zAxis( 0, 0, 1 );
  401.  
  402.     SoCoordinate3 *spinC = SO_GET_PART( this, "spineCoords", SoCoordinate3 );
  403.  
  404.     if ( paramDist > 1.0 ) {
  405.     int last = spinC->point.getNum() - 1;
  406.     center = spinC->point[ last ];
  407.     rot = getSpinePointRotation( last, last, 0.0 );
  408.     return;
  409.     }
  410.     else if ( paramDist < 0.0 ) {
  411.     center = spinC->point[ 0 ];
  412.     rot = getSpinePointRotation( 0, 0, 0.0 );
  413.     return;
  414.     }
  415.  
  416.     for ( int i = 0; i < spinC->point.getNum() - 1; i++ ) {
  417.     
  418.     p1 = spinC->point[i];
  419.     p2 = spinC->point[i+1];
  420.     diff = p2 - p1;
  421.     nextLength = lengthSoFar + diff.length();
  422.  
  423.     if ( desiredDist >= lengthSoFar && desiredDist <= nextLength ) {
  424.         float fraction = (desiredDist - lengthSoFar) 
  425.                  / (nextLength - lengthSoFar);
  426.         center = p1 + fraction * ( p2 - p1 );
  427.         rot = getSpinePointRotation( i, i+1, fraction );
  428.         return;
  429.     }
  430.  
  431.     lengthSoFar = nextLength;
  432.     }
  433. }
  434.  
  435. SbRotation
  436. GeneralizedCylinder::getSpinePointRotation(int ind0, int ind1, float paramDist )
  437. {
  438.     SbRotation rot0, rot1, interpedRot;
  439.  
  440.     if ( ind0 == ind1 )
  441.     return( spineKeyPointRotations[ind0]);
  442.     else {
  443.     rot0 = spineKeyPointRotations[ind0];
  444.     rot1 = spineKeyPointRotations[ind1];
  445.  
  446.     interpedRot = SbRotation::slerp( rot0, rot1, paramDist );
  447.  
  448.     return interpedRot;
  449.     }
  450. }
  451.  
  452. void 
  453. GeneralizedCylinder::initSpineKeyPointRotations()
  454. {
  455.     SoCoordinate3 *spinC = SO_GET_PART( this, "spineCoords", SoCoordinate3 );
  456.  
  457.     if ( spinC->point.getNum() > numAllocedSpineKeyRots ) {
  458.     if ( spineKeyPointRotations != NULL )
  459.         delete [] spineKeyPointRotations;
  460.     spineKeyPointRotations = new SbRotation[spinC->point.getNum()];
  461.         numAllocedSpineKeyRots = spinC->point.getNum();
  462.     }
  463.  
  464.     if (spinC->point.getNum() <= 0)
  465.     return;
  466.  
  467.     if (spinC->point.getNum() == 1) {
  468.     spineKeyPointRotations[0] = SbRotation::identity();
  469.     return;
  470.     }
  471.  
  472.     SbVec3f xAxis( 1, 0, 0 );
  473.     SbVec3f yAxis( 0, 1, 0 );
  474.     SbVec3f zAxis( 0, 0, 1 );
  475.     SbVec3f zeroVec( 0, 0, 0 );
  476.     SbVec3f diff1, diff2;
  477.  
  478.     if (spinC->point.getNum() == 2) {
  479.     diff1 = spinC->point[1] - spinC->point[0];
  480.     diff1.normalize();
  481.     spineKeyPointRotations[0].setValue( yAxis, diff1 );
  482.     spineKeyPointRotations[1] = spineKeyPointRotations[0];
  483.     return;
  484.     }
  485.  
  486.     // More than two points...
  487.     SbVec3f rotatedX, rotatedY, rotatedZ, prevRotatedZ;
  488.     SbVec3f p0, p1, p2;
  489.     int     lastInd = spinC->point.getNum() - 1;
  490.  
  491.     // Find rotation for First point...
  492.  
  493.     // First, treat case where spine is a closed polygon
  494.     if ( spineClosed.getValue() ) {
  495.         // Z axis is perp to plane formed by three points around p[0]
  496.         diff1 = spinC->point[lastInd-1] - spinC->point[0];
  497.         diff2 = spinC->point[1] - spinC->point[0];
  498.         rotatedZ = diff2.cross( diff1 );
  499.         // If 3 pts colinear, don't do this, but go on to other algorithm...
  500.         if ( rotatedZ != zeroVec ) {
  501.         rotatedZ.normalize();
  502.         // But make it lean towards the Z axis of the previous point.
  503.         if ( rotatedZ.dot( zAxis ) < 0 )
  504.             rotatedZ *= -1.0;
  505.  
  506.         // X axis is cross product of (nextPt-prevPt) with Z axis.
  507.         diff1 = spinC->point[1] - spinC->point[lastInd-1];
  508.         rotatedX = diff1.cross( rotatedZ );
  509.         rotatedX.normalize();
  510.  
  511.         rotatedY = rotatedZ.cross( rotatedX );
  512.         }
  513.     }
  514.  
  515.     // Spine not closed, or it was closed but couldn't get rotation...
  516.     if ( rotatedZ == zeroVec || !spineClosed.getValue() ) {
  517.         // Y axis just points from point 0 to first point that differs.
  518.         int i = 0;
  519.         p0 = p1 = spinC->point[i++];
  520.         while (p0 == p1 && i <= lastInd )
  521.         p1 = spinC->point[i++];
  522.         if ( p0 == p1 ) {
  523.         // all points were the same. Make everyone have no rotation.
  524.         for ( int j = 0; j <= lastInd; j++ ) {
  525.             spineKeyPointRotations[j] = SbRotation::identity();
  526.         }
  527.         return;
  528.         }
  529.         rotatedY = p1 - p0;
  530.         rotatedY.normalize();
  531.  
  532.         // Z axis is perpendicular to plane formed by 1st three points.
  533.         // Repeat as long as the two vectors are colinear.
  534.         diff1 = p0 - p1;
  535.         diff1.normalize();
  536.         rotatedZ = zeroVec;
  537.         while (rotatedZ == zeroVec && i <= lastInd ) {
  538.         p2 = spinC->point[i++];
  539.         diff2 = p2 - p1;
  540.         diff2.normalize();
  541.         rotatedZ = diff2.cross( diff1 );
  542.         }
  543.         if ( rotatedZ == zeroVec ) {
  544.         // all points coplanar. Make everyone have the same rotation.
  545.         for ( int j = 0; j <= lastInd; j++ ) {
  546.             spineKeyPointRotations[j].setValue( yAxis, rotatedY );
  547.         }
  548.         return;
  549.         }
  550.         rotatedZ.normalize();
  551.         // But make it lean towards positive Z each time.
  552.         if ( rotatedZ.dot( zAxis ) < 0 )
  553.         rotatedZ *= -1.0;
  554.  
  555.         // X axis is calculated based on Y and Z
  556.         rotatedX = rotatedY.cross( rotatedZ );
  557.     }
  558.  
  559.     SbMatrix firstAnswerMat(rotatedX[0],rotatedX[1],rotatedX[2],0,
  560.                     rotatedY[0],rotatedY[1],rotatedY[2],0,
  561.                     rotatedZ[0],rotatedZ[1],rotatedZ[2],0,
  562.                                      0 ,         0 ,         0 ,1);
  563.     spineKeyPointRotations[0].setValue(firstAnswerMat);
  564.  
  565.  
  566.     prevRotatedZ = rotatedZ;
  567.  
  568.     // Find rotation for all but first and last points...
  569.     for ( int ind = 1; ind < lastInd; ind++ ) {
  570.     // Z axis is perpendicular to plane formed by three points surrounding
  571.     // This one.
  572.     diff1 = spinC->point[ind-1] - spinC->point[ind];
  573.     diff2 = spinC->point[ind+1] - spinC->point[ind];
  574.     diff1.normalize();
  575.     diff2.normalize();
  576.     rotatedZ = diff2.cross( diff1 );
  577.     if (rotatedZ == zeroVec)      // if colinear, use previous rotatedZ
  578.         rotatedZ = prevRotatedZ;
  579.     rotatedZ.normalize();
  580.     // But make it lean towards the Z axis of the previous point.
  581.     if ( rotatedZ.dot( prevRotatedZ ) < 0.0 )
  582.         rotatedZ *= -1.0;
  583.  
  584.     // X axis is cross product of (nextPt-prevPt) with Z axis.
  585.     diff1 = spinC->point[ind+1] - spinC->point[ind-1];
  586.     if ( diff1 != zeroVec ) {
  587.         diff1.normalize();
  588.         rotatedX = diff1.cross( rotatedZ );
  589.         rotatedX.normalize();
  590.     }
  591.     // if diff1 = 0, just use previous value for rotatedX
  592.  
  593.     // Y axis is calculated based on X and Z
  594.     rotatedY = rotatedZ.cross( rotatedX );
  595.  
  596.     SbMatrix answerMat(rotatedX[0],rotatedX[1],rotatedX[2],0,
  597.                        rotatedY[0],rotatedY[1],rotatedY[2],0,
  598.                        rotatedZ[0],rotatedZ[1],rotatedZ[2],0,
  599.                                 0 ,         0 ,         0 ,1);
  600.     spineKeyPointRotations[ind].setValue(answerMat);
  601.  
  602.     prevRotatedZ = rotatedZ;
  603.     }
  604.  
  605.     // Find rotation for Last point...
  606.     if ( spineClosed.getValue() ) {
  607.         spineKeyPointRotations[lastInd] = spineKeyPointRotations[0];
  608.     }
  609.     else {
  610.         // Y axis just points from next-to-last point to last point
  611.         diff1 = spinC->point[lastInd] - spinC->point[lastInd - 1];
  612.         if (diff1 != zeroVec )
  613.         rotatedY = diff1;
  614.         // otherwise use previous value...
  615.         rotatedY.normalize();
  616.  
  617.         // Z axis is same as previous value.
  618.         rotatedZ = prevRotatedZ;
  619.  
  620.         // X axis is calculated based on Y and Z
  621.         rotatedX = rotatedY.cross( rotatedZ );
  622.  
  623.         SbMatrix lastAnswerMat(rotatedX[0],rotatedX[1],rotatedX[2],0,
  624.                    rotatedY[0],rotatedY[1],rotatedY[2],0,
  625.                    rotatedZ[0],rotatedZ[1],rotatedZ[2],0,
  626.                         0 ,         0 ,         0 ,1);
  627.         spineKeyPointRotations[lastInd].setValue(lastAnswerMat);
  628.     }
  629. }
  630.  
  631. void 
  632. GeneralizedCylinder::getTwistInfo( float paramDist, SbRotation &twistRot )
  633. {
  634.     // X coordinate determines amount of twist
  635.  
  636.     float desiredHeight = paramDist * twistHeight + twistMinY;
  637.  
  638.     float thisHeight, nextHeight, thisAngle, nextAngle;
  639.     float angle, fraction;
  640.     SbVec3f yAxis( 0, 1, 0 );
  641.  
  642.     if ( paramDist >= 1.0 ) {
  643.     twistRot = SbRotation( yAxis, twistMaxAngle );
  644.     return;
  645.     }
  646.     else if ( paramDist <= 0.0 ) {
  647.     twistRot = SbRotation( yAxis, twistMinAngle );
  648.     return;
  649.     }
  650.  
  651.     SoCoordinate3 *twisC = SO_GET_PART( this, "twistCoords", SoCoordinate3 );
  652.  
  653.     for ( int i = 0; i < twisC->point.getNum() - 1; i++ ) {
  654.     
  655.     thisHeight = twisC->point[i][1];
  656.     nextHeight = twisC->point[i+1][1];
  657.  
  658.     if ( desiredHeight >= thisHeight && desiredHeight <= nextHeight ) {
  659.         fraction = (desiredHeight - thisHeight) / (nextHeight - thisHeight);
  660.         thisAngle = twisC->point[i][0];
  661.         nextAngle = twisC->point[i+1][0];
  662.         angle = thisAngle + fraction * ( nextAngle - thisAngle );
  663.  
  664.         twistRot = SbRotation( yAxis, angle );
  665.         return;
  666.     }
  667.     if ( desiredHeight >= nextHeight && desiredHeight <= thisHeight ) {
  668.         fraction = (desiredHeight - nextHeight) / (thisHeight - nextHeight);
  669.         thisAngle = twisC->point[i][0];
  670.         nextAngle = twisC->point[i+1][0];
  671.         angle = nextAngle + fraction * ( thisAngle - nextAngle );
  672.  
  673.         twistRot = SbRotation( yAxis, angle );
  674.         return;
  675.     }
  676.     }
  677.  
  678.     twistRot = SbRotation::identity();
  679.     return;
  680. }
  681.  
  682. void 
  683. GeneralizedCylinder::initUpdateInfo()
  684. {
  685.     profileMinY = 100000000;
  686.     float maxY = -100000000;
  687.  
  688.     float val;
  689.  
  690.     SoCoordinate3 *profC = SO_GET_PART( this, "profileCoords", SoCoordinate3 );
  691.     SoCoordinate3 *crosC = SO_GET_PART( this, "crossSectionCoords", SoCoordinate3 );
  692.     SoCoordinate3 *spinC = SO_GET_PART( this, "spineCoords", SoCoordinate3 );
  693.     SoCoordinate3 *twisC = SO_GET_PART( this, "twistCoords", SoCoordinate3 );
  694.  
  695.     for ( int i = 0; i < profC->point.getNum(); i++ ) {
  696.     val = profC->point[i][1];
  697.     if ( profileMinY > val )
  698.         profileMinY = val;
  699.     if ( maxY < val )
  700.         maxY = val;
  701.     }
  702.  
  703.     if ( maxY == profileMinY )
  704.     maxY += 1.0;
  705.     profileHeight = maxY - profileMinY; 
  706.     profileOverHeight = 1.0 / profileHeight;
  707.  
  708.     //Spine length, and parametric spine distance of each point on spine.
  709.     if ( spineParamDistances != NULL )
  710.     delete [] spineParamDistances;
  711.     spineParamDistances = new float[spinC->point.getNum()];
  712.     spineLength = 0.0;
  713.     spineParamDistances[0] = 0;
  714.     SbVec3f diff;
  715.     for ( i = 0; i < spinC->point.getNum() - 1; i++ ) {
  716.     diff = spinC->point[i+1] - spinC->point[i];
  717.     spineLength += diff.length();
  718.     spineParamDistances[i+1] = spineLength; // absolute distance
  719.     }
  720.     if ( spineLength != 0.0 ) {
  721.     // convert absolute distance into parametric distance.
  722.     for ( i = 0; i < spinC->point.getNum(); i++ )
  723.         spineParamDistances[i] = spineParamDistances[i] / spineLength;
  724.     }
  725.  
  726.     // Rotations at key points of the spine...
  727.     initSpineKeyPointRotations();
  728.  
  729.     //Profile length
  730.     profileLength = 0.0;
  731.     for ( i = 0; i < profC->point.getNum() - 1; i++ ) {
  732.     diff = profC->point[i+1] - profC->point[i];
  733.     profileLength += diff.length();
  734.     }
  735.  
  736.     //CrossSection length
  737.     crossSectionLength = 0.0;
  738.     for ( i = 0; i < crosC->point.getNum() - 1; i++ ) {
  739.     diff = crosC->point[i+1] - crosC->point[i];
  740.     crossSectionLength += diff.length();
  741.     }
  742.  
  743.     float xVal, zVal;
  744.     float maxX = -100000000;
  745.     float maxZ = -100000000;
  746.     crossSectionMinX = crossSectionMinZ = 100000000;
  747.  
  748.     for ( i = 0; i < crosC->point.getNum(); i++ ) {
  749.     xVal = crosC->point[i][0];
  750.     if ( crossSectionMinX > xVal )
  751.         crossSectionMinX = xVal;
  752.     if ( maxX < xVal )
  753.         maxX = xVal;
  754.  
  755.     zVal = crosC->point[i][2];
  756.     if ( maxZ < zVal )
  757.         maxZ = zVal;
  758.     if ( crossSectionMinZ > zVal )
  759.         crossSectionMinZ = zVal;
  760.     }
  761.  
  762.     if ( maxX == crossSectionMinX )
  763.     maxX += 1.0;
  764.     if ( maxZ == crossSectionMinZ )
  765.     maxZ += 1.0;
  766.     crossSectionWidth = maxX - crossSectionMinX; 
  767.     crossSectionDepth = maxZ - crossSectionMinZ; 
  768.  
  769.  
  770.  
  771.     twistMinY = 100000000;
  772.     maxY = -100000000;
  773.     twistMinAngle = 100000000;
  774.     twistMaxAngle = -100000000;
  775.     for ( i = 0; i < twisC->point.getNum(); i++ ) {
  776.     val = twisC->point[i][1];
  777.     if ( twistMinY > val )
  778.         twistMinY = val;
  779.     if ( maxY < val )
  780.         maxY = val;
  781.     val = twisC->point[i][0];
  782.     if ( twistMinAngle > val )
  783.         twistMinAngle = val;
  784.     if ( twistMaxAngle < val )
  785.         twistMaxAngle = val;
  786.     }
  787.     if ( maxY == twistMinY )
  788.     maxY += 1.0;
  789.     twistHeight = maxY - twistMinY; 
  790.  
  791.     calculateFullProfile();
  792.     calculateFullCrossSection();
  793. }
  794.  
  795. //
  796. // Given a set of 2D profile coordinates (the z-coordinate of this
  797. // Vec3f field is ignored), this creates a generalized cylinder
  798. // If passed NULL, this routine will use the last coordinates passed
  799. // (this is used when the number of sides in the generalized cylinder is
  800. // changed).
  801. //
  802.  
  803. void
  804. GeneralizedCylinder::updateSurface()
  805. {
  806.     initUpdateInfo();
  807.  
  808.     const SoMFVec3f *prof =  &fullProfile->point;
  809.     const SoMFVec3f *cross = &fullCrossSection->point;
  810.  
  811.     int numRows = prof->getNum();
  812.     int numCols = cross->getNum();
  813.  
  814.     SoFaceSet          *myFaceSet = NULL;
  815.     SoTriangleStripSet *myTStripSet = NULL;
  816.     SoQuadMesh         *myQMesh = NULL;
  817.     SoGroup            *myNurbsSurfs = NULL;
  818.  
  819.     GeneralizedCylinder::RenderShapeType sType
  820.         = (GeneralizedCylinder::RenderShapeType)renderShapeType.getValue();
  821.  
  822.     // DEAL WITH SHAPE HINTS
  823.     SoShapeHints *hints = (SoShapeHints *) getPart("shapeHints", FALSE);
  824.     if (hints) {
  825.     switch(sType) {
  826.         case BEZIER_SURFACE:
  827.         case CUBIC_SPLINE_SURFACE:
  828.         case CUBIC_TO_EDGE_SURFACE:
  829.         hints->vertexOrdering = SoShapeHints::UNKNOWN_ORDERING;
  830.         break;
  831.         case FACE_SET:
  832.         case TRIANGLE_STRIP_SET:
  833.         case QUAD_MESH:
  834.             hints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE;
  835.         break;
  836.         }
  837.     }
  838.  
  839.     SoCoordinate3 *coords = NULL;
  840.  
  841.     // DELETE AND GET THE CORRECT NODES.
  842.     if (withSides.getValue() == FALSE ) {
  843.     setPart( "faceSet", NULL );
  844.     setPart( "triangleStripSet", NULL );
  845.     setPart( "quadMesh", NULL );
  846.     setPart( "nurbsSurfaceGroup", NULL );
  847.     setPart( "coords", NULL );
  848.     }
  849.     else {
  850.     switch(sType) {
  851.         case FACE_SET:
  852.         myFaceSet = SO_GET_PART( this, "faceSet", SoFaceSet );
  853.         setPart( "triangleStripSet", NULL );
  854.         setPart( "quadMesh", NULL );
  855.         setPart( "nurbsSurfaceGroup", NULL );
  856.         break;
  857.         case TRIANGLE_STRIP_SET:
  858.         myTStripSet 
  859.             = SO_GET_PART(this,"triangleStripSet", SoTriangleStripSet );
  860.         setPart( "faceSet", NULL );
  861.         setPart( "quadMesh", NULL );
  862.         setPart( "nurbsSurfaceGroup", NULL );
  863.         break;
  864.         case QUAD_MESH:
  865.         myQMesh = SO_GET_PART( this, "quadMesh", SoQuadMesh );
  866.         setPart( "faceSet", NULL );
  867.         setPart( "triangleStripSet", NULL );
  868.         setPart( "nurbsSurfaceGroup", NULL );
  869.         break;
  870.         case BEZIER_SURFACE:
  871.         case CUBIC_SPLINE_SURFACE:
  872.         case CUBIC_TO_EDGE_SURFACE:
  873.         myNurbsSurfs = SO_GET_PART(this, "nurbsSurfaceGroup", SoGroup );
  874.         setPart( "faceSet", NULL );
  875.         setPart( "triangleStripSet", NULL );
  876.         setPart( "quadMesh", NULL );
  877.         break;
  878.     }
  879.         coords  = SO_GET_PART( this, "coords", SoCoordinate3 );
  880.     }
  881.  
  882.  
  883.     // FORGET IT IF TOO FEW POINTS
  884.     if ( numRows < 2 || numCols < 2 )  {
  885.     setPart("faceSet", NULL);
  886.     setPart("triangleStripSet", NULL );
  887.     setPart("quadMesh", NULL );
  888.     setPart("nurbsSurfaceGroup", NULL );
  889.  
  890.     setPart("topCapFaces", NULL);
  891.     setPart("bottomCapFaces", NULL);
  892.  
  893.     if (withSides.getValue())
  894.         coords->point.deleteValues( 0, -1 );
  895.     updateSurroundingManip();
  896.     return;
  897.     }
  898.  
  899.     // SET UP TOPOLOGICAL INFORMATION
  900.     int      numCoordsTotal;
  901.     SoMFLong *nV;
  902.     if ( withSides.getValue() ) {
  903.     switch (sType ) {
  904.         case QUAD_MESH:
  905.         // There will be (numRows * numCols) coordinates
  906.         numCoordsTotal = numRows * numCols;
  907.         myQMesh->verticesPerColumn = numRows;
  908.         myQMesh->verticesPerRow = numCols;
  909.         if (myQMesh->startIndex.getValue() != 0)
  910.             myQMesh->startIndex = 0;
  911.         break;
  912.         case TRIANGLE_STRIP_SET:
  913.         // Each strip (there are numRows-1 in all) has 2*numCols coordinates
  914.         numCoordsTotal = (numRows - 1) * (numCols * 2);
  915.  
  916.         nV = &myTStripSet->numVertices;
  917.  
  918.         if (nV->getNum() != (numRows-1)) {
  919.             nV->setNum(numRows-1);
  920.             for (int k = 0; k < (numRows-1); k++ )
  921.             nV->set1Value( k, (numCols*2) );
  922.         }
  923.         if (myTStripSet->startIndex.getValue() != 0)
  924.             myTStripSet->startIndex = 0;
  925.         break;
  926.         case FACE_SET:
  927.         // Each square is made of 2 tris, drawn with 3 coords. 
  928.         {
  929.             int numTris = 2 * (numRows - 1) * (numCols - 1);
  930.             numCoordsTotal = numTris * 3;
  931.  
  932.             nV = &myFaceSet->numVertices;
  933.             if (nV->getNum() != numTris) {
  934.             nV->setNum(numTris);
  935.             for (int k = 0; k < numTris; k++ )
  936.                 nV->set1Value( k, 3 );
  937.             }
  938.             if (myFaceSet->startIndex.getValue() != 0)
  939.             myFaceSet->startIndex = 0;
  940.         }
  941.         break;
  942.         case BEZIER_SURFACE:
  943.         case CUBIC_SPLINE_SURFACE:
  944.         case CUBIC_TO_EDGE_SURFACE:
  945.         {
  946.         // There will be (numRows * numCols) coordinates
  947.         numCoordsTotal = numRows * numCols;
  948.  
  949.         // Enlist a NurbMaker to create the surface
  950.         // patch nodes.
  951.         if (myNurbMaker == NULL)
  952.             myNurbMaker = new NurbMaker;
  953.         if (sType == BEZIER_SURFACE)
  954.             myNurbMaker->setPatchType( NurbMaker::BEZIER );
  955.         else if (sType == CUBIC_SPLINE_SURFACE) 
  956.             myNurbMaker->setPatchType( NurbMaker::CUBIC );
  957.         else 
  958.             myNurbMaker->setPatchType( NurbMaker::CUBIC_TO_EDGE );
  959.         myNurbMaker->setFlipNormals( normsFlipped.getValue() );
  960.  
  961.         SbVec2s closure( spineClosed.getValue(),
  962.                     crossSectionClosed.getValue());
  963.         myNurbMaker->setWraparound(closure);
  964.  
  965.         SbVec2s numMeshDivs(numCols, numRows);
  966.  
  967.         SoGroup *surfGroup;
  968.         surfGroup = myNurbMaker->createNurbsGroup( numMeshDivs, closure );
  969.         setPart("nurbsSurfaceGroup", surfGroup );
  970.         }
  971.         break;
  972.     }
  973.  
  974.     if (coords->point.getNum() < numCoordsTotal)
  975.         coords->point.insertSpace(0, numCoordsTotal - coords->point.getNum());
  976.     else if ( coords->point.getNum() > numCoordsTotal )
  977.         coords->point.deleteValues(0, coords->point.getNum() - numCoordsTotal );
  978.     }
  979.  
  980.     int topRow, botRow;
  981.  
  982.     static SbVec3f *topPoints = NULL;
  983.     static SbVec3f *botPoints = NULL;
  984.     static SbVec2f *topTex = NULL;
  985.     static SbVec2f *botTex = NULL;
  986.  
  987.     static int     p_alloc = 0;
  988.     if ( p_alloc != numCols ) {
  989.     if ( topPoints != NULL ) {
  990.         delete [] topPoints;
  991.         topPoints = NULL;
  992.     }
  993.     if ( botPoints != NULL ) {
  994.         delete [] botPoints;
  995.         botPoints = NULL;
  996.     }
  997.     if ( topTex != NULL ) {
  998.         delete [] topTex;
  999.         topTex = NULL;
  1000.     }
  1001.     if ( botTex != NULL ) {
  1002.         delete [] botTex;
  1003.         botTex = NULL;
  1004.     }
  1005.     p_alloc = 0;
  1006.     }
  1007.     if ( p_alloc == 0 ) {
  1008.     p_alloc = numCols;
  1009.     topPoints = new SbVec3f[p_alloc];
  1010.     botPoints = new SbVec3f[p_alloc];
  1011.     topTex = new SbVec2f[p_alloc];
  1012.     botTex = new SbVec2f[p_alloc];
  1013.     }
  1014.  
  1015.  
  1016.     // If either cap is being drawn, and texture is on...
  1017.     if (    withTextureCoords.getValue() 
  1018.      && ( withTopCap.getValue()    || withBottomCap.getValue() ) 
  1019.      && crossSectionClosed.getValue() && numCols >= 4 ) {
  1020.      loadTextureRow( -1, topTex );
  1021.          SoTextureCoordinate2 *cTC  
  1022.             = SO_GET_PART( this, "capTextureCoords", SoTextureCoordinate2 );
  1023.      cTC->point.setValues( 0, numCols, topTex );
  1024.     }
  1025.     else {
  1026.     setPart("capTextureCoords", NULL);
  1027.     }
  1028.  
  1029.     SoCoordinate3 *topCapC = SO_GET_PART(this,"topCapCoords",SoCoordinate3);
  1030.     SoIndexedFaceSet *topCapF = SO_GET_PART(this,"topCapFaces", SoIndexedFaceSet);
  1031.     SoCoordinate3 *botCapC = SO_GET_PART(this,"bottomCapCoords",SoCoordinate3);
  1032.     SoIndexedFaceSet *botCapF =SO_GET_PART(this,"bottomCapFaces", SoIndexedFaceSet);
  1033.  
  1034.     if ( withTopCap.getValue() && crossSectionClosed.getValue() 
  1035.      && numCols >= 4 ) {
  1036.  
  1037.  
  1038.     SoMFVec3f *topC = &topCapC->point;
  1039.     SoMFLong  *topF = &topCapF->coordIndex;
  1040.     SoMFLong  *topS = &topCapScratchFace->coordIndex;
  1041.  
  1042.     // Load the values of the section points into topCapC.
  1043.     // Leave out the last point, since it is a repeat of the first
  1044.     // (remember, crossSectionClosed == TRUE !)
  1045.     if ( numCols - 1 < topC->getNum() )
  1046.         topC->deleteValues( 0, topC->getNum() - (numCols - 1) );
  1047.     topC->setValues( 0, numCols - 1, cross->getValues(0) );
  1048.  
  1049.     // The triangulator likes the y values to be 0.0. So flatten the 
  1050.     // sucker out.
  1051.     SbVec3f *bunchaTopVecs = topC->startEditing();
  1052.     SbBool topVecsChanged = FALSE;
  1053.     for (int topCount = 0; topCount < topC->getNum(); topCount++ ) {
  1054.         SbVec3f theVec = (*topC)[topCount];
  1055.         if (bunchaTopVecs[topCount][1] != 0.0) {
  1056.             bunchaTopVecs[topCount][1] = 0.0;
  1057.         topVecsChanged = TRUE;
  1058.         }
  1059.     }
  1060.     bunchaTopVecs = NULL;
  1061.     if (topVecsChanged)
  1062.         topC->finishEditing();
  1063.  
  1064.     // Load the indices to draw the section as a single polygon into
  1065.     // topCapScratchFace
  1066.     if ( (topC->getNum() + 1) < topS->getNum() )
  1067.         topS->deleteValues( 0, topS->getNum() - (topC->getNum() + 1) );
  1068.     else if ( (topC->getNum() + 1) > topS->getNum() )
  1069.         topS->insertSpace( 0, (topC->getNum() + 1) - topS->getNum() );
  1070.     int long *scratchVals = topS->startEditing();
  1071.     for ( int i = 0; i < topC->getNum(); i++ ) {
  1072.         scratchVals[i] = i;
  1073.     }
  1074.     scratchVals[ topC->getNum() ] = -1;  // to close the polygon
  1075.     topS->finishEditing();
  1076.  
  1077.     // Before calling loadRow (i.e., while the points still lay in the 
  1078.     // y=0 plane), and before triangulating (i.e., while we still have
  1079.     // a record of a polygon which follows the entire cross section)
  1080.     // determine if the cross section is clockwise ordered. This will
  1081.     // affect the normal vector we select.
  1082.     SbBool isSectionClockWise;
  1083.     isSectionClockWise = Triangulator::clockWiseTest(
  1084.                     (*topC), (*topS), 0, topC->getNum());
  1085.  
  1086.     // Triangulate, putting result into topCapF
  1087.     Triangulator::triangulate( *topC, *topS, *topF );
  1088.  
  1089.     // Triangulation makes everything clockwise ordered.
  1090.     // Assure that normals are in the right direction...
  1091.     if ( normsFlipped.getValue() != isSectionClockWise ) {
  1092.         // reverse the order of the points in topF.  
  1093.         // Each polygon in topF should be a triangle at this point,
  1094.         // so reversing the order is simple.
  1095.         long *vals = topF->startEditing();
  1096.         long swapTemp;
  1097.         for ( int ind = 0; ind < topF->getNum(); ind += 4 ) {
  1098.         // Switch ind and ind+2 (ind+3 is a -1 to signal end of poly)
  1099.         swapTemp = vals[ind];
  1100.         vals[ind] = vals[ind+2];
  1101.         vals[ind+2] = swapTemp;
  1102.         }
  1103.         topF->finishEditing();
  1104.     }
  1105.  
  1106.     // Check that, after triangulation, there are still polygons left...
  1107.     if ( topF->getNum() > 0 ) {
  1108.  
  1109.         // Transform the values for the points in the section as they
  1110.         // appear in the final row 
  1111.         topC->insertSpace( 0, 1 ); // Add room for an extra point at the 
  1112.                        // end since loadRow will put it there...
  1113.         SbVec3f *topCapPoints = topC->startEditing();
  1114.         loadRow( 0, topCapPoints );
  1115.         topC->finishEditing();
  1116.     }
  1117.     }
  1118.     else {
  1119.     // Clear out the coord and face set for top cap
  1120.     topCapC->point.deleteValues( 0, -1 );
  1121.     topCapF->coordIndex.deleteValues( 0, -1 );
  1122.     }
  1123.  
  1124.     if ( withBottomCap.getValue() && crossSectionClosed.getValue() 
  1125.      && numCols >= 4 ) {
  1126.  
  1127.     SoMFVec3f *botC = &botCapC->point;
  1128.     SoMFLong  *botF = &botCapF->coordIndex;
  1129.     SoMFLong  *botS = &bottomCapScratchFace->coordIndex;
  1130.  
  1131.     // Load the values of the section points into botCapC.
  1132.     // Leave out the last point, since it is a repeat of the first
  1133.     // (remember, crossSectionClosed == TRUE !)
  1134.     if ( numCols - 1 < botC->getNum() )
  1135.         botC->deleteValues( 0, botC->getNum() - (numCols - 1) );
  1136.     botC->setValues( 0, numCols - 1, cross->getValues(0) );
  1137.  
  1138.     // The triangulator likes the y values to be 0.0. So flatten the 
  1139.     // sucker out.
  1140.     SbVec3f *bunchaBotVecs = botC->startEditing();
  1141.     SbBool botVecsChanged = FALSE;
  1142.     for (int botCount = 0; botCount < botC->getNum(); botCount++ ) {
  1143.         SbVec3f theVec = (*botC)[botCount];
  1144.         if (bunchaBotVecs[botCount][1] != 0.0) {
  1145.             bunchaBotVecs[botCount][1] = 0.0;
  1146.         botVecsChanged = TRUE;
  1147.         }
  1148.     }
  1149.     bunchaBotVecs = NULL;
  1150.     if (botVecsChanged)
  1151.         botC->finishEditing();
  1152.  
  1153.     // Load the indices to draw the section as a single polygon into
  1154.     // bottomCapScratchFace
  1155.     if ( (botC->getNum() + 1) < botS->getNum() )
  1156.         botS->deleteValues( 0, botS->getNum() - (botC->getNum() + 1) );
  1157.     else if ( (botC->getNum() + 1) > botS->getNum() )
  1158.         botS->insertSpace( 0, (botC->getNum() + 1) - botS->getNum() );
  1159.     int long *scratchVals = botS->startEditing();
  1160.     for ( int i = 0; i < botC->getNum(); i++ ) {
  1161.         scratchVals[i] = i;
  1162.     }
  1163.     scratchVals[ botC->getNum() ] = -1;  // to close the polygon
  1164.     botS->finishEditing();
  1165.  
  1166.     // Before calling loadRow (i.e., while the points still lay in the 
  1167.     // y=0 plane), and before triangulating (i.e., while we still have
  1168.     // a record of a polygon which follows the entire cross section)
  1169.     // determine if the cross section is clockwise ordered. This will
  1170.     // affect the normal vector we select.
  1171.     SbBool isSectionClockWise;
  1172.     isSectionClockWise = Triangulator::clockWiseTest(
  1173.                     (*botC), (*botS), 0, botC->getNum());
  1174.  
  1175.     // Triangulate, putting result into bottomCapFaces
  1176.     Triangulator::triangulate( *botC, *botS, *botF );
  1177.  
  1178.     // Triangulation makes everything clockwise ordered.
  1179.     // Assure that normals are in the right direction...
  1180.     if ( normsFlipped.getValue() == isSectionClockWise ) {
  1181.         // reverse the order of the points in botF.  
  1182.         // Each polygon in topF should be a triangle at this point,
  1183.         // so reversing the order is simple.
  1184.         long *vals = botF->startEditing();
  1185.         long swapTemp;
  1186.         for ( int ind = 0; ind < botF->getNum(); ind += 4 ) {
  1187.         // Switch ind and ind+2 (ind+3 is a -1 to signal end of poly)
  1188.         swapTemp = vals[ind];
  1189.         vals[ind] = vals[ind+2];
  1190.         vals[ind+2] = swapTemp;
  1191.         }
  1192.         botF->finishEditing();
  1193.     }
  1194.  
  1195.     // Check that, after triangulation, there are still polygons left...
  1196.     if ( botF->getNum() > 0 ) {
  1197.  
  1198.         // Transform the values for the points in the section as they
  1199.         // appear in the final row 
  1200.         botC->insertSpace( 0, 1 ); // Add room for an extra point at the 
  1201.                        // end, since loadRow will put it there.
  1202.         SbVec3f *bottomCapPoints = botC->startEditing();
  1203.         loadRow( numRows - 1, bottomCapPoints );
  1204.         botC->finishEditing();
  1205.     }
  1206.     }
  1207.  
  1208.     else {
  1209.     // Clear out the coord and face set for bottom cap
  1210.     botCapC->point.deleteValues( 0, -1 );
  1211.     botCapF->coordIndex.deleteValues( 0, -1 );
  1212.     }
  1213.  
  1214.  
  1215.  
  1216.     if ( withSides.getValue() ) {
  1217.  
  1218.     SbVec2f *tCEdit;
  1219.     SoTextureCoordinate2 *tCN; 
  1220.     if ( withTextureCoords.getValue() ) {
  1221.         tCN = SO_GET_PART(this, "textureCoords", SoTextureCoordinate2 );
  1222.         if (tCN->point.getNum() < numCoordsTotal)
  1223.         tCN->point.insertSpace(0, numCoordsTotal - tCN->point.getNum());
  1224.         else if ( tCN->point.getNum() > numCoordsTotal )
  1225.         tCN->point.deleteValues(0, tCN->point.getNum() - numCoordsTotal );
  1226.         tCEdit = tCN->point.startEditing();
  1227.     }
  1228.     else {
  1229.         setPart("textureCoords", NULL);
  1230.     }
  1231.  
  1232.     SbVec3f *coordsEdit = coords->point.startEditing();
  1233.     int curCoord = 0;
  1234.     int curTexCoord = 0;
  1235.  
  1236.     for (topRow = 0, botRow = 1; topRow < numRows - 1; topRow++ , botRow++ ){
  1237.         if ( topRow == 0 ) {
  1238.         // calculate topRow
  1239.         loadRow( topRow, topPoints );
  1240.         if ( withTextureCoords.getValue() )
  1241.             loadTextureRow( topRow, topTex );
  1242.         }
  1243.         else {
  1244.         // switch rows, so old botRow becomes new topRow
  1245.         SbVec3f *temp = topPoints;
  1246.         topPoints = botPoints;
  1247.         botPoints = temp;
  1248.         if ( withTextureCoords.getValue() ) {
  1249.             SbVec2f *temp2 = topTex;
  1250.             topTex = botTex;
  1251.             botTex = temp2;
  1252.         }
  1253.         }
  1254.  
  1255.         // calculate botRow 
  1256.         loadRow( botRow, botPoints );
  1257.         if ( withTextureCoords.getValue() )
  1258.         loadTextureRow( botRow, botTex );
  1259.  
  1260.  
  1261.         // put the coordinates for this row triangles into the node...
  1262.         SbBool flipped = normsFlipped.getValue();
  1263.  
  1264.         switch(sType) {
  1265.         case QUAD_MESH:
  1266.         case BEZIER_SURFACE:
  1267.         case CUBIC_SPLINE_SURFACE:
  1268.         case CUBIC_TO_EDGE_SURFACE:
  1269.             // For these two, load only coords for the bottom row.
  1270.             // However load the top row also the first time through
  1271.             {
  1272.             int curCol;
  1273.             if (topRow == 0) {
  1274.             if ( flipped ) 
  1275.                 for (curCol = 0; curCol < numCols; curCol++ )
  1276.                 coordsEdit[curCoord++] = topPoints[curCol];
  1277.             else
  1278.                 for (curCol = numCols-1; curCol >= 0; curCol-- )
  1279.                 coordsEdit[curCoord++] = topPoints[curCol];
  1280.             }
  1281.  
  1282.             // load bottom row
  1283.             if ( flipped ) 
  1284.             for (curCol = 0; curCol < numCols; curCol++ )
  1285.                 coordsEdit[curCoord++] = botPoints[curCol];
  1286.             else
  1287.             for (curCol = numCols-1; curCol >= 0; curCol-- )
  1288.                 coordsEdit[curCoord++] = botPoints[curCol];
  1289.             }
  1290.             break;
  1291.         case TRIANGLE_STRIP_SET:
  1292.             {
  1293.             for( int curCol = 0; curCol < numCols; curCol++ ) {
  1294.                 if ( ! flipped ) {
  1295.                 coordsEdit[curCoord++] = topPoints[curCol];
  1296.                 coordsEdit[curCoord++] = botPoints[curCol];
  1297.                 }
  1298.                 else {
  1299.                 coordsEdit[curCoord++] = botPoints[curCol];
  1300.                 coordsEdit[curCoord++] = topPoints[curCol];
  1301.                 }
  1302.             }
  1303.             }
  1304.             break;
  1305.         case FACE_SET:
  1306.             int leftCol, rightCol;
  1307.             for ( leftCol = 0,  rightCol = 1; leftCol < numCols - 1;
  1308.              leftCol++, rightCol++ ) {
  1309.  
  1310.             if (  ! flipped ) {
  1311.                 // first draw the top left triangle
  1312.                 coordsEdit[curCoord++] = topPoints[leftCol];
  1313.                 coordsEdit[curCoord++] = topPoints[rightCol];
  1314.                 coordsEdit[curCoord++] = botPoints[leftCol];
  1315.  
  1316.                 // then draw the bottom right triangle
  1317.                 coordsEdit[curCoord++] = botPoints[leftCol];
  1318.                 coordsEdit[curCoord++] = topPoints[rightCol];
  1319.                 coordsEdit[curCoord++] = botPoints[rightCol];
  1320.             }
  1321.             else {
  1322.                 // first draw the top left triangle
  1323.                 coordsEdit[curCoord++] = botPoints[leftCol];
  1324.                 coordsEdit[curCoord++] = topPoints[rightCol];
  1325.                 coordsEdit[curCoord++] = topPoints[leftCol];
  1326.  
  1327.                 // then draw the bottom right triangle
  1328.                 coordsEdit[curCoord++] = botPoints[rightCol];
  1329.                 coordsEdit[curCoord++] = topPoints[rightCol];
  1330.                 coordsEdit[curCoord++] = botPoints[leftCol];
  1331.             }
  1332.             }
  1333.             break;
  1334.         }
  1335.  
  1336.         if ( withTextureCoords.getValue() ) {
  1337.         switch(sType) {
  1338.             case QUAD_MESH:
  1339.             case BEZIER_SURFACE:
  1340.             case CUBIC_SPLINE_SURFACE:
  1341.             case CUBIC_TO_EDGE_SURFACE:
  1342.             // For these two, load only coords for the bottom row.
  1343.             // However load the top row also the first time through
  1344.             {
  1345.                 int curCol;
  1346.                 if (topRow == 0) {
  1347.                 if ( flipped ) 
  1348.                     for (curCol = 0; curCol < numCols; curCol++ )
  1349.                     tCEdit[curTexCoord++] = topTex[curCol];
  1350.                 else
  1351.                     for (curCol = numCols-1; curCol >= 0; curCol-- )
  1352.                     tCEdit[curTexCoord++] = topTex[curCol];
  1353.                 }
  1354.  
  1355.                 // load bottom row
  1356.                 if ( flipped ) 
  1357.                 for (curCol = 0; curCol < numCols; curCol++ )
  1358.                     tCEdit[curTexCoord++] = botTex[curCol];
  1359.                 else
  1360.                 for (curCol = numCols-1; curCol >= 0; curCol-- )
  1361.                     tCEdit[curTexCoord++] = botTex[curCol];
  1362.             }
  1363.             break;
  1364.             case TRIANGLE_STRIP_SET:
  1365.             {
  1366.                 for (int curCol=0; curCol < numCols;curCol++){
  1367.                 if ( ! flipped ) {
  1368.                     tCEdit[ curTexCoord++] = topTex[curCol];
  1369.                     tCEdit[ curTexCoord++] = botTex[curCol];
  1370.                 }
  1371.                 else {
  1372.                     tCEdit[ curTexCoord++] = botTex[curCol];
  1373.                     tCEdit[ curTexCoord++] = topTex[curCol];
  1374.                 }
  1375.                 }
  1376.             }
  1377.             break;
  1378.             case FACE_SET:
  1379.             int leftCol, rightCol;
  1380.  
  1381.             // set the texture coordinates for these triangles...
  1382.             for ( leftCol = 0,  rightCol = 1; leftCol < numCols - 1;
  1383.                  leftCol++, rightCol++ ) {
  1384.  
  1385.                 if (  ! flipped ) {
  1386.                 // first draw the top left triangle
  1387.                 tCEdit[ curTexCoord++] = topTex[leftCol];
  1388.                 tCEdit[ curTexCoord++] = topTex[rightCol];
  1389.                 tCEdit[ curTexCoord++] = botTex[leftCol];
  1390.  
  1391.                 // then draw the bottom right triangle
  1392.                 tCEdit[ curTexCoord++] = botTex[leftCol];
  1393.                 tCEdit[ curTexCoord++] = topTex[rightCol];
  1394.                 tCEdit[ curTexCoord++] = botTex[rightCol];
  1395.                 }
  1396.                 else {
  1397.                 // first draw the top left triangle
  1398.                 tCEdit[ curTexCoord++] = botTex[leftCol];
  1399.                 tCEdit[ curTexCoord++] = topTex[rightCol];
  1400.                 tCEdit[ curTexCoord++] = topTex[leftCol];
  1401.  
  1402.                 // then draw the bottom right triangle
  1403.                 tCEdit[ curTexCoord++] = botTex[rightCol];
  1404.                 tCEdit[ curTexCoord++] = topTex[rightCol];
  1405.                 tCEdit[ curTexCoord++] = botTex[leftCol];
  1406.                 }
  1407.             }
  1408.             break;
  1409.         }
  1410.         }
  1411.     }
  1412.     coords->point.finishEditing();
  1413.     if ( withTextureCoords.getValue() ) {
  1414.         tCN->point.finishEditing();
  1415.     }
  1416.     }
  1417.  
  1418.     // If there's a manip as our transform, tell it to change size.
  1419.     updateSurroundingManip();
  1420. }
  1421.  
  1422. SoSeparator *
  1423. GeneralizedCylinder::makeVanillaVersion()
  1424. {
  1425.     // Start with a nice update!
  1426.     updateSurface();
  1427.  
  1428.     SoSeparator *answer = new SoSeparator;
  1429.     answer->ref();
  1430.  
  1431.     SoNode *newKid;
  1432.  
  1433.     // Go through all the relevant parts. If you find them, add them as 
  1434.     // a child to the answer.
  1435.  
  1436.     if ( newKid = getPart( "callbackList", FALSE ))
  1437.     answer->addChild( newKid );
  1438.     if ( newKid = getPart( "pickStyle", FALSE ))
  1439.     answer->addChild( newKid );
  1440.  
  1441.     if ( newKid = getPart( "appearance.lightModel", FALSE ))
  1442.     answer->addChild( newKid );
  1443.     if ( newKid = getPart( "appearance.environment", FALSE ))
  1444.     answer->addChild( newKid );
  1445.     if ( newKid = getPart( "appearance.drawStyle", FALSE ))
  1446.     answer->addChild( newKid );
  1447.     if ( newKid = getPart( "appearance.material", FALSE ))
  1448.     answer->addChild( newKid );
  1449.     if ( newKid = getPart( "appearance.complexity", FALSE ))
  1450.     answer->addChild( newKid );
  1451.     if ( newKid = getPart( "appearance.texture2", FALSE ))
  1452.     answer->addChild( newKid );
  1453.     if ( newKid = getPart( "appearance.font", FALSE ))
  1454.     answer->addChild( newKid );
  1455.  
  1456.     if ( newKid = getPart( "units", FALSE ))
  1457.     answer->addChild( newKid );
  1458.     if ( newKid = getPart( "transform", FALSE ))
  1459.     answer->addChild( newKid );
  1460.     if ( newKid = getPart( "texture2Transform", FALSE ))
  1461.     answer->addChild( newKid );
  1462.  
  1463.     if ( newKid = getPart( "shapeHints", FALSE ))
  1464.     answer->addChild( newKid );
  1465.     if ( newKid = getPart( "textureBinding", FALSE ))
  1466.     answer->addChild( newKid );
  1467.     if ( newKid = getPart( "textureCoords", FALSE ))
  1468.     answer->addChild( newKid );
  1469.     if ( newKid = getPart( "texture2", FALSE ))
  1470.     answer->addChild( newKid );
  1471.     if ( newKid = getPart( "coords", FALSE ))
  1472.     answer->addChild( newKid );
  1473.     if ( newKid = getPart( "faceSet", FALSE ))
  1474.     answer->addChild( newKid );
  1475.     if ( newKid = getPart( "quadMesh", FALSE ))
  1476.     answer->addChild( newKid );
  1477.     if ( newKid = getPart( "nurbsSurfaceGroup", FALSE ))
  1478.     answer->addChild( newKid );
  1479.     if ( newKid = getPart( "triangleStripSet", FALSE ))
  1480.     answer->addChild( newKid );
  1481.     if ( newKid = getPart( "capTextureBinding", FALSE ))
  1482.     answer->addChild( newKid );
  1483.     if ( newKid = getPart( "capTextureCoords", FALSE ))
  1484.     answer->addChild( newKid );
  1485.     if ( newKid = getPart( "topCapCoords", FALSE ))
  1486.     answer->addChild( newKid );
  1487.     if ( newKid = getPart( "topCapFaces", FALSE ))
  1488.     answer->addChild( newKid );
  1489.     if ( newKid = getPart( "bottomCapCoords", FALSE ))
  1490.     answer->addChild( newKid );
  1491.     if ( newKid = getPart( "bottomCapFaces", FALSE ))
  1492.     answer->addChild( newKid );
  1493.  
  1494.     if ( newKid = getPart( "childList", FALSE ))
  1495.     answer->addChild( newKid );
  1496.  
  1497.     answer->unrefNoDelete();
  1498.     return answer;
  1499. }
  1500.  
  1501. void
  1502. GeneralizedCylinder::changeCurveClosure( char *curveName, SbBool newClosed )
  1503. {
  1504.     SoCoordinate3 *coords = SO_GET_PART( this, curveName, SoCoordinate3 );
  1505.  
  1506.     SoMFVec3f *cField = &coords->point;
  1507.     int numC = cField->getNum();
  1508.     if ( newClosed == TRUE ) {
  1509.     if ( numC > 0 ) {
  1510.         // make a new final point that is a copy of the zero'th
  1511.         if ( numC > 1 && ((*cField)[0] == (*cField)[numC-1] ))
  1512.         // First make sure that the first and last points aren't already
  1513.         // already the same.
  1514.         return;
  1515.         else
  1516.         cField->set1Value( numC, (*cField)[0] );
  1517.     }
  1518.     }
  1519.     else if ( numC > 1 ) {
  1520.     // remove the last point
  1521.     if ( (*cField)[0] != (*cField)[numC-1] )
  1522.         // First make sure that the first and last points are the same.
  1523.         return;
  1524.     else
  1525.         cField->deleteValues( numC - 1, 1 );
  1526.     }
  1527. }
  1528.  
  1529. void
  1530. GeneralizedCylinder::changeWithTextureCoords( SbBool newWith )
  1531. {
  1532.     if ( withTextureCoords.getValue() != newWith )
  1533.          withTextureCoords = newWith;
  1534.  
  1535.     if ( newWith ) {
  1536.     set( "textureBinding { value PER_VERTEX }");
  1537.     set( "capTextureBinding { value PER_VERTEX_INDEXED }");
  1538.     set( "texture2 { filename \"defaultTexture.rgb\" }" );
  1539.     }
  1540.     else {
  1541.  
  1542.     // The texture2 and texture2Transform parts may have some useful
  1543.     // information, so we check them before deleting. 
  1544.     // The others can just go bye-bye
  1545.  
  1546.     SoTexture2 *tex2 = (SoTexture2 *) getPart("texture2",FALSE);
  1547.     if ( tex2 != NULL ) {
  1548.         tex2->filename = "";
  1549.         if (tex2->hasDefaultValues())
  1550.             setPart("texture2", NULL );
  1551.     }
  1552.     SoTexture2Transform *txXf 
  1553.         = (SoTexture2Transform *) getPart("texture2Transform",FALSE);
  1554.     if ( txXf != NULL ) {
  1555.         if (txXf->hasDefaultValues())
  1556.             setPart("texture2Transform", NULL );
  1557.     }
  1558.  
  1559.     setPart("textureBinding", NULL );
  1560.     setPart("capTextureBinding", NULL );
  1561.  
  1562.     setPart("textureCoords", NULL );
  1563.     setPart("capTextureCoords", NULL );
  1564.     }
  1565. }
  1566.  
  1567. void
  1568. GeneralizedCylinder::calculateFullProfile()
  1569. {
  1570.     SoCoordinate3 *profC = SO_GET_PART( this, "profileCoords", SoCoordinate3 );
  1571.     SoCoordinate3 *spinC = SO_GET_PART( this, "spineCoords", SoCoordinate3 );
  1572.     SoCoordinate3 *twisC = SO_GET_PART( this, "twistCoords", SoCoordinate3 );
  1573.  
  1574.     int numInProf = profC->point.getNum();
  1575.  
  1576.     // Start by just copying profC into fullProfile...
  1577.     int i = numInProf - fullProfile->point.getNum();
  1578.     if ( i > 0 )
  1579.     fullProfile->point.insertSpace(0, i);
  1580.     else if ( i < 0 )
  1581.     fullProfile->point.deleteValues(0, -i);
  1582.     fullProfile->point.setValues(0,numInProf,profC->point.getValues(0));
  1583.  
  1584.     // Next, add in any points in the spine curve that are not demarcated
  1585.     // in the profile curve.
  1586.     for ( i = 0; i < spinC->point.getNum(); i++ ) {
  1587.     // Get parametric distance of point along length of spine.
  1588.     float pDist  = spineParamDistances[i];
  1589.     float pHeight = profileMinY + pDist * profileHeight;
  1590.  
  1591.     // Translate this parametric distance into a point or points in 
  1592.     // the profile curve.
  1593.     SbVec3f p0, p1, profPoint;
  1594.     float   amt;
  1595.     for ( int j = 0; j < fullProfile->point.getNum() - 1; j++ ) {
  1596.         p0 = fullProfile->point[j];
  1597.         p1 = fullProfile->point[j+1];
  1598.         // If the spine point falls at a point along the profile that
  1599.         // lies between p0 and p1, then interpolate and add it in.
  1600.         if ( pHeight > p0[1] && pHeight < p1[1] ) {
  1601.         amt = (pHeight - p0[1]) / (p1[1] - p0[1]);
  1602.         profPoint = p0 + amt * ( p1 - p0);
  1603.         // Add the point
  1604.         fullProfile->point.insertSpace(j+1,1);
  1605.         fullProfile->point.set1Value(j+1,profPoint);
  1606.         j++;
  1607.         }
  1608.         else if ( pHeight < p0[1] && pHeight > p1[1] ) {
  1609.         amt = (pHeight - p1[1]) / (p0[1] - p1[1]);
  1610.         profPoint = p1 + amt * ( p0 - p1);
  1611.         // Add the point
  1612.         fullProfile->point.insertSpace(j+1,1);
  1613.         fullProfile->point.set1Value(j+1,profPoint);
  1614.         j++;
  1615.         }
  1616.     }
  1617.     }
  1618.  
  1619.     // Next, add in any points in the twist curve that are not demarcated
  1620.     // in the profile curve.
  1621.     for ( i = 0; i < twisC->point.getNum(); i++ ) {
  1622.     }
  1623.  
  1624.  
  1625.     if ( numInProf < 2 )
  1626.     return;
  1627.  
  1628.     if ( minNumRows.getValue() > numInProf ) {
  1629.     
  1630.     float overDist = profileLength / minNumRows.getValue();
  1631.     SbVec3f p0, p1, newPt, diff;
  1632.     float length;
  1633.     for ( int i = 0; i < fullProfile->point.getNum() - 1; i++ ) {
  1634.         p0 = fullProfile->point[i];
  1635.         p1 = fullProfile->point[i+1];
  1636.         diff = p1 - p0;
  1637.         length = diff.length();
  1638.         diff.normalize();
  1639.         if ( length > overDist ) {
  1640.         newPt = p0 + diff * overDist;
  1641.         fullProfile->point.insertSpace(i+1,1);
  1642.         fullProfile->point.set1Value(i+1,newPt);
  1643.         }
  1644.     }
  1645.     }
  1646. }
  1647. void
  1648. GeneralizedCylinder::calculateFullCrossSection()
  1649. {
  1650.     SoCoordinate3 *crosC = SO_GET_PART( this, "crossSectionCoords", SoCoordinate3 );
  1651.  
  1652.     int numInCross = crosC->point.getNum();
  1653.  
  1654.     // Start by just copying crosC into fullCrossSection...
  1655.     int i = numInCross - fullCrossSection->point.getNum();
  1656.     if ( i > 0 )
  1657.     fullCrossSection->point.insertSpace(0, i);
  1658.     else if ( i < 0 )
  1659.     fullCrossSection->point.deleteValues(0, -i);
  1660.     fullCrossSection->point.setValues( 0, numInCross, crosC->point.getValues(0));
  1661.  
  1662.     if ( numInCross < 2 )
  1663.     return;
  1664.  
  1665.     if ( minNumCols.getValue() > numInCross ) {
  1666.     
  1667.     float overDist = crossSectionLength / minNumCols.getValue();
  1668.     SbVec3f p0, p1, newPt, diff;
  1669.     float length;
  1670.     for ( int i = 0; i < fullCrossSection->point.getNum() - 1; i++ ) {
  1671.         p0 = fullCrossSection->point[i];
  1672.         p1 = fullCrossSection->point[i+1];
  1673.         diff = p1 - p0;
  1674.         length = diff.length();
  1675.         diff.normalize();
  1676.         if ( length > overDist ) {
  1677.         newPt = p0 + diff * overDist;
  1678.         fullCrossSection->point.insertSpace(i+1,1);
  1679.         fullCrossSection->point.set1Value(i+1,newPt);
  1680.         }
  1681.     }
  1682.     }
  1683. }
  1684.  
  1685. void
  1686. GeneralizedCylinder::fieldsChangedCB(void *data, SoSensor *sens )
  1687. {
  1688.     if ( ((SoDataSensor *)sens)->getTriggerNode() != data )
  1689.     return;
  1690.  
  1691.     GeneralizedCylinder *s = (GeneralizedCylinder *)data;
  1692.  
  1693.     SoField *triggerField = ((SoDataSensor *)sens)->getTriggerField();
  1694.     SbBool flagFieldChanged = FALSE;
  1695.  
  1696.     if (triggerField == &s->renderShapeType)         
  1697.      flagFieldChanged = TRUE;
  1698.     else if (triggerField == &s->normsFlipped)       
  1699.     flagFieldChanged = TRUE;
  1700.     else if (triggerField == &s->profileClosed)      
  1701.     flagFieldChanged = TRUE;
  1702.     else if (triggerField == &s->crossSectionClosed) 
  1703.     flagFieldChanged = TRUE;
  1704.     else if (triggerField == &s->spineClosed)        
  1705.     flagFieldChanged = TRUE;
  1706.     else if (triggerField == &s->twistClosed)        
  1707.     flagFieldChanged = TRUE;
  1708.     else if (triggerField == &s->minNumRows)         
  1709.     flagFieldChanged = TRUE;
  1710.     else if (triggerField == &s->minNumCols)         
  1711.     flagFieldChanged = TRUE;
  1712.     else if (triggerField == &s->withSides)         
  1713.     flagFieldChanged = TRUE;
  1714.     else if (triggerField == &s->withTopCap)         
  1715.     flagFieldChanged = TRUE;
  1716.     else if (triggerField == &s->withBottomCap)      
  1717.     flagFieldChanged = TRUE;
  1718.     else if (triggerField == &s->withTextureCoords)  
  1719.     flagFieldChanged = TRUE;
  1720.  
  1721.     if (flagFieldChanged == FALSE )
  1722.     return;
  1723.  
  1724.     // Special stuff to do if a curve's closurehas been changed
  1725.     if ( s->profileClosed.getValue() != s->profileAlreadyClosed ) {
  1726.     s->changeCurveClosure( "profileCoords", s->profileClosed.getValue() );
  1727.     s->profileAlreadyClosed = s->profileClosed.getValue();
  1728.     }
  1729.     if ( s->crossSectionClosed.getValue() != s->crossSectionAlreadyClosed ) {
  1730.     s->changeCurveClosure( "crossSectionCoords", 
  1731.                 s->crossSectionClosed.getValue() );
  1732.     s->crossSectionAlreadyClosed = s->crossSectionClosed.getValue();
  1733.     }
  1734.     if ( s->spineClosed.getValue() != s->spineAlreadyClosed ) {
  1735.     s->changeCurveClosure( "spineCoords", s->spineClosed.getValue() );
  1736.     s->spineAlreadyClosed = s->spineClosed.getValue();
  1737.     }
  1738.     if ( s->twistClosed.getValue() != s->twistAlreadyClosed ) {
  1739.     s->changeCurveClosure( "twistCoords", s->twistClosed.getValue() );
  1740.     s->twistAlreadyClosed = s->twistClosed.getValue();
  1741.     }
  1742.  
  1743.     // Special stuff to do if a curve's texture status has been changed
  1744.     if ( s->withTextureCoords.getValue() != s->alreadyWithTextureCoords) {
  1745.     s->changeWithTextureCoords( s->withTextureCoords.getValue() );
  1746.     s->alreadyWithTextureCoords = s->withTextureCoords.getValue();
  1747.     }
  1748.  
  1749.     s->updateSurface();
  1750. }
  1751.  
  1752. void
  1753. GeneralizedCylinder::inputChangedCB(void *data, SoSensor *sens )
  1754. {
  1755.     GeneralizedCylinder *s = (GeneralizedCylinder *)data;
  1756.     SoNode *notifier = ((SoNodeSensor *)sens)->getTriggerNode();
  1757.  
  1758.     SoCoordinate3 *coordPart = NULL;
  1759.     SbBool        isClosed;
  1760.  
  1761.     coordPart = SO_CHECK_PART( s, "profileCoords", SoCoordinate3 );
  1762.     if ( notifier == coordPart )
  1763.     isClosed = s->profileClosed.getValue();
  1764.     else {
  1765.     coordPart = SO_CHECK_PART( s, "crossSectionCoords", SoCoordinate3 );
  1766.         if ( notifier == coordPart )
  1767.         isClosed = s->crossSectionClosed.getValue();
  1768.     else {
  1769.         coordPart = SO_CHECK_PART( s, "spineCoords", SoCoordinate3 );
  1770.         if ( notifier == coordPart )
  1771.         isClosed = s->spineClosed.getValue();
  1772.         else {
  1773.         coordPart = SO_CHECK_PART( s, "twistCoords", SoCoordinate3 );
  1774.         if ( notifier == coordPart )
  1775.             isClosed = s->twistClosed.getValue();
  1776.         else
  1777.             coordPart = NULL;
  1778.         }
  1779.     }
  1780.     }
  1781.  
  1782.     if (coordPart == NULL)
  1783.     return;
  1784.  
  1785.     if (isClosed) {
  1786.     SoMFVec3f *cField = &(coordPart->point);
  1787.     int numC = cField->getNum();
  1788.     // make the last point follow the first point.
  1789.     if ( numC == 3 )
  1790.         cField->set1Value( numC, (*cField)[0] );
  1791.     else if ( numC > 3 && (*cField)[0] != (*cField)[numC - 1] )
  1792.         cField->set1Value( numC - 1, (*cField)[0] );
  1793.     }
  1794.     s->updateSurface();
  1795. }
  1796.  
  1797.  
  1798. void 
  1799. GeneralizedCylinder::updateSurroundingManip()
  1800. {
  1801.     SoNode *n;
  1802.  
  1803.     // Is there a transform?
  1804.     n = getPart( "transform", FALSE);
  1805.     if (n==NULL) return;
  1806.  
  1807.     // If it's not a transform manip, then return...
  1808.     if ( !n->isOfType( SoTransformManip::getClassTypeId() ) ) 
  1809.     return;
  1810.  
  1811.     SoTransformManip *xfm = (SoTransformManip *) n;
  1812.  
  1813.     // Try to get the dragger from the manip.
  1814.     SoDragger *d = xfm->getDragger();
  1815.     if (d==NULL)
  1816.     return;
  1817.  
  1818.     SoSurroundScale *ss 
  1819.     = (SoSurroundScale *) d->getPart( "surroundScale", FALSE );
  1820.  
  1821.     if ( ss != NULL)
  1822.     ss->invalidate();
  1823. }
  1824.  
  1825. void 
  1826. GeneralizedCylinder::changeTransformType( SoType newType )
  1827. {
  1828.     // Return if manip is already of correct type.
  1829.     SoTransform *curXf = (SoTransform *) getPart("transform",FALSE);
  1830.     if ( curXf && (curXf->getTypeId() == newType ))
  1831.     return;
  1832.  
  1833.  
  1834.     if (   newType.isBad() || 
  1835.      ! newType.isDerivedFrom(SoTransformManip::getClassTypeId() ) ||
  1836.      ! newType.canCreateInstance() ) {
  1837.  
  1838.     // If no transform part, just return:
  1839.         SoFullPath *xfPath = (SoFullPath *) createPathToPart("transform",FALSE);
  1840.     if ( ! xfPath )
  1841.         return;
  1842.  
  1843.     // If tail is not an SoTransformManip, just return.
  1844.     SoNode *tail = xfPath->getTail();
  1845.     if ( ! tail->isOfType( SoTransformManip::getClassTypeId() ) )
  1846.         return;
  1847.  
  1848.     // Tail is an SoTransformManip.  Tell it to replace itself with 
  1849.     // a regular transform
  1850.     tail->ref();
  1851.     SoTransformManip *m = (SoTransformManip *) tail;
  1852.     m->replaceManip(xfPath, new SoTransform );
  1853.     tail->unref();
  1854.     xfPath->unref();
  1855.     }
  1856.     else {
  1857.  
  1858.     // Make a manip of given type, tell it to insert itself in the path
  1859.         SoPath *xfPath = createPathToPart("transform",TRUE);
  1860.     SoTransformManip *m = (SoTransformManip *) newType.createInstance();
  1861.     xfPath->ref();
  1862.     m->replaceNode(xfPath );
  1863.         xfPath->unref();
  1864.     }
  1865. }
  1866.